#pragma once
#include <xutility>

template<typename T, int Align = 16>
class Array
{
public:
	Array() : mCapacity(0), mSize(0), mData(NULL) { }

	~Array()
	{
		if (mData != NULL)
			_aligned_free(mData);
	}

	void Add(const T& value)
	{
		if (mSize == mCapacity)
		{
			size_t wanted = mCapacity + mCapacity / 2;	// try to grow by 50%
			if (wanted < mSize + 1)
				wanted = mSize + 1;
			Reserve(wanted);
		}

		mData[mSize++] = value;
	}

	void SwapRemove(size_t idx)
	{
		if (idx >= mSize)
			return;

		if (idx + 1 != mSize)
			mData[idx] = mData[mSize - 1];

		mSize--;
	}

	void Clear(bool destroy = false)
	{
		mSize = 0;

		if (destroy)
		{
			if (mData != NULL)
				_aligned_free(mData);
			mCapacity = 0;
		}
	}

	void Reserve(size_t count)
	{
		if (mCapacity >= count)
			return;

		// not enough room, reallocate
		T* ptr = (T*)_aligned_malloc(sizeof(T) * count, Align);
		if (!Empty())
			memcpy(ptr, mData, sizeof(T) * mSize);

		if (mData != NULL)
			_aligned_free(mData);

		mCapacity = count;
		mData = ptr;
	}

	void Resize(size_t size)
	{
		Resize(size, T());
	}

	void Resize(size_t size, const T& value)
	{
		if (mSize == size)
			return;

		if (mSize > size)
		{
			// TODO: call the destructors for no scaler value.
		}
		else
		{
			Reserve(size);
			for (size_t i = mSize; i < size; i++)
				mData[i] = value;
		}

		mSize = size;
	}

	T& Get(size_t idx)
	{
		assert(idx < mSize);
		return mData[idx];
	}

	const T& Get(size_t idx) const
	{
		assert(idx < mSize);
		return mData[idx];
	}

	size_t Capacity() const { return mCapacity; }
	size_t Size() const { return mSize; }
	bool Empty() const { return mSize == 0; }

private:
	size_t mCapacity;
	size_t mSize;
	T* mData;
};
